/******************************************************************************
 *
 * $RCSfile: $
 * $Revision: $
 *
 * This module provides interface routines to the LPC ARM UARTs.
 * Copyright 2004, R O SoftWare
 * No guarantees, warrantees, or promises, implied or otherwise.
 * May be used for hobby or commercial purposes provided copyright
 * notice remains intact.
 *
 * reduced to see what has to be done for minimum UART-support by mthomas
 *****************************************************************************/

// #warning "this is a reduced version of the R O Software code"

#include "lpc24xx.h"
#include "uart.h"

/* on LPC210x: UART0 TX-Pin=P0.2, RX-Pin=P0.1 
   PINSEL0 has to be set to "UART-Function" = Function "01" 
   for Pin 0.0 and 0.1 */
   
#define PINSEL_BITPIN0  0
#define PINSEL_BITPIN1  2
// #define PINSEL_BITPIN2  4
#define PINSEL_FIRST_ALT_FUNC   1
// #define PINSEL_SECOND_ALT_FUNC   2

// Values of Bits 0-3 in PINSEL to activate UART0
#define UART0_PINSEL    ((PINSEL_FIRST_ALT_FUNC<<PINSEL_BITPIN0)|(PINSEL_FIRST_ALT_FUNC<<PINSEL_BITPIN1))
// Mask of Bits 0-4
#define UART0_PINMASK      (0x0000000F)    /* PINSEL0 Mask for UART0 */

// U0_LCR devisor latch bit 
#define UART0_LCR_DLAB  7

/*    baudrate divisor - use UART_BAUD macro
 *    mode - see typical modes (uart.h)
 *    fmode - see typical fmodes (uart.h)
 *    NOTE: uart0Init(UART_BAUD(9600), UART_8N1, UART_FIFO_8); 
 */
#if 0
	void uart0Init(uint16_t baud, uint8_t mode, uint8_t fmode)
	{
	  // setup Pin Function Select Register (Pin Connect Block)
	  // make sure old values of Bits 0-4 are masked out and
	  // set them according to UART0-Pin-Selection
	  PINSEL0 = (PINSEL0 & ~UART0_PINMASK) | UART0_PINSEL;

	  U0IER = 0x00;             // disable all interrupts
	  U0IIR = 0x00;             // clear interrupt ID register
	  U0LSR = 0x00;             // clear line status register

	  // set the baudrate - DLAB must be set to access DLL/DLM
	  U0LCR = (1<<UART0_LCR_DLAB); // set divisor latches (DLAB)
	  U0DLL = (uint8_t)baud;         // set for baud low byte
	  U0DLM = (uint8_t)(baud >> 8);  // set for baud high byte

	  // set the number of characters and other
	  // user specified operating parameters
	  // Databits, Parity, Stopbits - Settings in Line Control Register
	  U0LCR = (mode & ~(1<<UART0_LCR_DLAB)); // clear DLAB "on-the-fly"
	  // setup FIFO Control Register (fifo-enabled + xx trig)
	  U0FCR = fmode;
	}
#endif
/* This is the working function. */
void UART0_init() {
	PCONP = (PCONP | (1 << 3)); // Enable PCUART0

	PCLKSEL0 = (1 << 7); // Bits [7:6] of PCLK_SEL0 should be 10 for /2 divider (32 MHz)

	U0LCR = (3 | (1 << 7)); // Set DLAB = 1 in U0LCR
	U0DLM = 0x0; // from calculator
	U0DLL = 0x7d; // 125, from calculator
	//U0DLL = 0x4a;
	U0FDR = 0x87; // set fractional baud rate ratio (from calculator)
	//U0FDR = 0xc7;

	U0FCR = UART_FIFO_8; // Enable FIFO and set mode to every 1 character.

	//Step 5. Select UART pins and pin modes
	PINSEL0 = (PINSEL0 | ((1 << 4) + (1 << 6))); // [5:4] TXD0... [7:6] RXD0

	PINMODE0 |= 0xA0;//TXD0 and RXD0 are set as having neither a pull up nor pull down resistor

	U0LCR = (3 | (0 << 7)); // Set DLAB = 0
}

int uart0Putch(int ch)
{
  while (!(U0LSR & ULSR_THRE))          // wait for TX buffer to empty
    continue;                           // also either WDOG() or swap()

  U0THR = (uint8_t)ch;  // put char to Transmit Holding Register
  return (uint8_t)ch;      // return char ("stdio-compatible"?)
}

const char *uprintf(const char *string)
{
	char ch;
	
	while ((ch = *string)) {
		if (uart0Putch(ch)<0) break;
		string++;
	}
	
	return string;
}

const char* itox_i(int n) {
	static char temp[16];
	char *buf = &temp[0];
	int pos;
	int i;
	pos = 0;

	buf[0] = '0';
	buf[1] = 'x';

	pos=2;

	for (i = 2*sizeof(int) - 1; i >= 0; i--) {
		buf[pos] = "0123456789ABCDEF"[((n >> i*4) & 0xF)];
		pos++;
	}
	buf[pos] = '\0';

	return buf;
}

const char* itox_dword(DWORD n) {
	static char temp[16];
	char *buf = &temp[0];
	int pos;
	int i;
	pos = 0;

	buf[0] = '0';
	buf[1] = 'x';

	pos=2;

	for (i = 2*sizeof(DWORD) - 1; i >= 0; i--) {
		buf[pos] = "0123456789ABCDEF"[((n >> i*4) & 0xF)];
		pos++;
	}
	buf[pos] = '\0';

	return buf;
}

const char* itox_word(WORD n) {
	static char temp[16];
	char *buf = &temp[0];
	int pos;
	int i;
	pos = 0;

	buf[0] = '0';
	buf[1] = 'x';

	pos=2;

	for (i = 2*sizeof(WORD) - 1; i >= 0; i--) {
		buf[pos] = "0123456789ABCDEF"[((n >> i*4) & 0xF)];
		pos++;
	}
	buf[pos] = '\0';

	return buf;
}

/* Prints the hex representation of an int to UART */
void uprintfx_int(int i) {
	uprintf(itox_i(i));
}

/* Prints the hex representation of a DWORD to UART */
void uprintfx_dword(DWORD i) {
	uprintf(itox_dword(i));
}

/* Prints the hex representation of a WORD to UART */
void uprintfx_word(WORD i) {
	uprintf(itox_word(i));
}

/* Helper function for uprintf_i */
void int_to_string(int i,char *s)	// Convert Integer to String
{
	char sign;short len;char *p;
	sign='+';len=0;p=s;
	if(i<0){sign='-';i=-i;}
	do{*s=(i%10)+'0';s++;len++;i/=10;}while(i!=0);
	if(sign=='-'){*s='-';s++;len++;}
	for(i=0;i<len/2;i++){p[len]=p[i];p[i]=p[len-1-i];p[len-1-i]=p[len];}
	p[len]=0;
}

/* Prints an integer to UART0 */
void uprintfi(int i)
{
	char s[7];
	int_to_string(i,s);
	uprintf(s);
}

int uart0TxEmpty(void)
{
  return (U0LSR & (ULSR_THRE | ULSR_TEMT)) == (ULSR_THRE | ULSR_TEMT);
}

void uart0TxFlush(void)
{
  U0FCR |= UFCR_TX_FIFO_RESET;          // clear the TX fifo
}


/* Returns: character on success, -1 if no character is available */
int uart0Getch(void)
{
	if (U0LSR & ULSR_RDR)                 // check if character is available
		return U0RBR;                     // return character

	return -1;
}

/* Returns: character on success, waits */
int uart0GetchW(void)
{
	while ( !(U0LSR & ULSR_RDR) ); // wait for character 
	return U0RBR;                // return character
}


#if _USE_XFUNC_OUT
#include <stdarg.h>
void (*xfunc_out)(unsigned char);	/* Pointer to the output stream */
static char *outptr;

/*----------------------------------------------*/
/* Put a character                              */
/*----------------------------------------------*/

void xputc (char c)
{
	uart0Putch(c);
	return;

	if (_CR_CRLF && c == '\n') xputc('\r');		/* CR -> CRLF */

	if (outptr) {
		*outptr++ = (unsigned char)c;
		return;
	}

	if (xfunc_out) xfunc_out((unsigned char)c);
}



/*----------------------------------------------*/
/* Put a null-terminated string                 */
/*----------------------------------------------*/

void xputs (const char* str)
{
	/*while (*str)
		xputc(*str++);
	*/
	uprintf(str);
}



/*----------------------------------------------*/
/* Formatted string output                      */
/*----------------------------------------------*/
/*  xprintf("%d", 1234);			"1234"
    xprintf("%6d,%3d%%", -200, 5);	"  -200,  5%"
    xprintf("%-6u", 100);			"100   "
    xprintf("%ld", 12345678L);		"12345678"
    xprintf("%04x", 0xA3);			"00a3"
    xprintf("%08LX", 0x123ABC);		"00123ABC"
    xprintf("%016b", 0x550F);		"0101010100001111"
    xprintf("%s", "String");		"String"
    xprintf("%-4s", "abc");			"abc "
    xprintf("%4s", "abc");			" abc"
    xprintf("%c", 'a');				"a"
    xprintf("%f", 10.0);            <xprintf lacks floating point support>
*/

static
void xvprintf (
	const char*	fmt,	/* Pointer to the format string */
	va_list arp			/* Pointer to arguments */
)
{
	unsigned int r, i, j, w, f;
	unsigned long v;
	char s[16], c, d, *p;


	for (;;) {
		c = *fmt++;					/* Get a char */
		if (!c) break;				/* End of format? */
		if (c != '%') {				/* Pass through it if not a % sequense */
			xputc(c); continue;
		}
		f = 0;
		c = *fmt++;					/* Get first char of the sequense */
		if (c == '0') {				/* Flag: '0' padded */
			f = 1; c = *fmt++;
		} else {
			if (c == '-') {			/* Flag: left justified */
				f = 2; c = *fmt++;
			}
		}
		for (w = 0; c >= '0' && c <= '9'; c = *fmt++)	/* Minimum width */
			w = w * 10 + c - '0';
		if (c == 'l' || c == 'L') {	/* Prefix: Size is long int */
			f |= 4; c = *fmt++;
		}
		if (!c) break;				/* End of format? */
		d = c;
		if (d >= 'a') d -= 0x20;
		switch (d) {				/* Type is... */
		case 'S' :					/* String */
			p = va_arg(arp, char*);
			for (j = 0; p[j]; j++) ;
			while (!(f & 2) && j++ < w) xputc(' ');
			xputs(p);
			while (j++ < w) xputc(' ');
			continue;
		case 'C' :					/* Character */
			xputc((char)va_arg(arp, int)); continue;
		case 'B' :					/* Binary */
			r = 2; break;
		case 'O' :					/* Octal */
			r = 8; break;
		case 'D' :					/* Signed decimal */
		case 'U' :					/* Unsigned decimal */
			r = 10; break;
		case 'X' :					/* Hexdecimal */
			r = 16; break;
		default:					/* Unknown type (passthrough) */
			xputc(c); continue;
		}

		/* Get an argument and put it in numeral */
		v = (f & 4) ? va_arg(arp, long) : ((d == 'D') ? (long)va_arg(arp, int) : (long)va_arg(arp, unsigned int));
		if (d == 'D' && (v & 0x80000000)) {
			v = 0 - v;
			f |= 8;
		}
		i = 0;
		do {
			d = (char)(v % r); v /= r;
			if (d > 9) d += (c == 'x') ? 0x27 : 0x07;
			s[i++] = d + '0';
		} while (v && i < sizeof(s));
		if (f & 8) s[i++] = '-';
		j = i; d = (f & 1) ? '0' : ' ';
		while (!(f & 2) && j++ < w) xputc(d);
		do xputc(s[--i]); while(i);
		while (j++ < w) xputc(' ');
	}
}


void xprintf (
	const char*	fmt,	/* Pointer to the format string */
	...					/* Optional arguments */
)
{
	va_list arp;


	va_start(arp, fmt);
	xvprintf(fmt, arp);
	va_end(arp);
}


void xsprintf (
	char* buff,			/* Pointer to the output buffer */
	const char*	fmt,	/* Pointer to the format string */
	...					/* Optional arguments */
)
{
	va_list arp;


	outptr = buff;		/* Switch destination for memory */

	va_start(arp, fmt);
	xvprintf(fmt, arp);
	va_end(arp);

	*outptr = 0;		/* Terminate output string */
	outptr = 0;			/* Switch destination for device */
}


void xdprintf (
	void(*func)(unsigned char),	/* Pointer to the output function */
	const char*	fmt,			/* Pointer to the format string */
	...							/* Optional arguments */
)
{
	va_list arp;
	void (*pf)(unsigned char);


	pf = xfunc_out;		/* Save current output device */
	xfunc_out = func;	/* Switch output to specified device */

	va_start(arp, fmt);
	xvprintf(fmt, arp);
	va_end(arp);

	xfunc_out = pf;		/* Restore output device */
}



/*----------------------------------------------*/
/* Dump a line of binary dump                   */
/*----------------------------------------------*/

void put_dump (
	const void* buff,		/* Pointer to the byte array to be dumped */
	unsigned long addr,		/* Heading address value */
	int len					/* Number of bytes to be dumped */
)
{
	int i;
	const unsigned char *p = buff;


	xprintf("%08lX ", addr);		/* address */

	for (i = 0; i < len; i++)		/* data (hexdecimal) */
		xprintf(" %02X", p[i]);

	xputc(' ');
	for (i = 0; i < len; i++)		/* data (ascii) */
		xputc((p[i] >= ' ' && p[i] <= '~') ? p[i] : '.');

	xputc('\n');
}

#endif /* _USE_XFUNC_OUT */



#if _USE_XFUNC_IN
unsigned char (*xfunc_in)(void);	/* Pointer to the input stream */

/*----------------------------------------------*/
/* Get a line from the input                    */
/*----------------------------------------------*/

int xgets (		/* 0:End of stream, 1:A line arrived */
	char* buff,	/* Pointer to the buffer */
	int len		/* Buffer length */
)
{
	int c, i;


	if (!xfunc_in) return 0;		/* No input function specified */

	i = 0;
	for (;;) {
		c = xfunc_in();				/* Get a char from the incoming stream */
		if (!c) return 0;			/* End of stream? */
		if (c == '\r') break;		/* End of line? */
		if (c == '\b' && i) {		/* Back space? */
			i--;
			if (_LINE_ECHO) xputc(c);
			continue;
		}
		if (c >= ' ' && i < len - 1) {	/* Visible chars */
			buff[i++] = c;
			if (_LINE_ECHO) xputc(c);
		}
	}
	buff[i] = 0;	/* Terminate with a \0 */
	if (_LINE_ECHO) xputc('\n');
	return 1;
}


int xdgets (	/* 0:End of stream, 1:A line arrived */
	unsigned char (*func)(void),	/* Pointer to the input stream function */
	char* buff,	/* Pointer to the buffer */
	int len		/* Buffer length */
)
{
	unsigned char (*pf)(void);
	int n;


	pf = xfunc_in;			/* Save current input device */
	xfunc_in = func;		/* Switch input to specified device */
	n = xgets(buff, len);	/* Get a line */
	xfunc_in = pf;			/* Restore input device */

	return n;
}


/*----------------------------------------------*/
/* Get a value of the string                    */
/*----------------------------------------------*/
/*	"123 -5   0x3ff 0b1111 0377  w "
	    ^                           1st call returns 123 and next ptr
	       ^                        2nd call returns -5 and next ptr
                   ^                3rd call returns 1023 and next ptr
                          ^         4th call returns 15 and next ptr
                               ^    5th call returns 255 and next ptr
                                  ^ 6th call fails and returns 0
*/

int xatoi (			/* 0:Failed, 1:Successful */
	char **str,		/* Pointer to pointer to the string */
	long *res		/* Pointer to the valiable to store the value */
)
{
	unsigned long val;
	unsigned char c, r, s = 0;


	*res = 0;

	while ((c = **str) == ' ') (*str)++;	/* Skip leading spaces */

	if (c == '-') {		/* negative? */
		s = 1;
		c = *(++(*str));
	}

	if (c == '0') {
		c = *(++(*str));
		switch (c) {
		case 'x':		/* hexdecimal */
			r = 16; c = *(++(*str));
			break;
		case 'b':		/* binary */
			r = 2; c = *(++(*str));
			break;
		default:
			if (c <= ' ') return 1;	/* single zero */
			if (c < '0' || c > '9') return 0;	/* invalid char */
			r = 8;		/* octal */
		}
	} else {
		if (c < '0' || c > '9') return 0;	/* EOL or invalid char */
		r = 10;			/* decimal */
	}

	val = 0;
	while (c > ' ') {
		if (c >= 'a') c -= 0x20;
		c -= '0';
		if (c >= 17) {
			c -= 7;
			if (c <= 9) return 0;	/* invalid char */
		}
		if (c >= r) return 0;		/* invalid char for current radix */
		val = val * r + c;
		c = *(++(*str));
	}
	if (s) val = 0 - val;			/* apply sign if needed */

	*res = val;
	return 1;
}

#endif /* _USE_XFUNC_IN */
